﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Supermodel.DomainModel;
using Supermodel.Exceptions;
using Supermodel.MVC.Extensions;
using Supermodel.Reflection;
using Supermodel.Repository;

namespace Supermodel.MVC.FormModels
{
    public abstract class MultiSelectFormModelBase : IComparable, IFormModelModelBinder, IFormModelEditorTemplate, IFormModelEntityMapper
    {
        public class Option
        {
            public Option(string value, string label) : this(value, label, false) { }
            public Option(string value, string label, bool selected)
            {
                Value = value;
                Label = label;
                Selected = selected;
            }
            public string Value { get; private set; }
            public string Label { get; private set; }
            public bool Selected { get; set; }
        }
        public List<Option> Options = new List<Option>();

        public List<SelectListItem> GetSelectListItemList()
        {
            var selectListItemList = new List<SelectListItem>();
            foreach (var option in Options)
            {
                var item = new SelectListItem { Value = option.Value, Text = option.Label, Selected = option.Selected };
                selectListItemList.Add(item);
            }
            return selectListItemList;
        }

        public void MapFromEntityObject(object obj, Type objType)
        {
            if (ReflectionHelpers.IsCollectionOfEntities(objType)) throw new FormModelPropertyCantBeAutomapedException(string.Format("{0} can't be automapped to {1}", GetType().Name, objType.Name));

            Options = ((ISqlDataRepoGenericTypeIgnorant)RepoFactory.CreateForRuntimeType(objType.GetGenericArguments()[0])).GetMultiSelectOptions();
            if (obj == null) return;
            foreach (var entity in (IEnumerable<EntityCore>)obj)
            {
                // ReSharper disable AccessToModifiedClosure
                var match = Options.Find(x => x.Value == entity.Id.ToString());
                // ReSharper restore AccessToModifiedClosure
                if (match != null) match.Selected = true;
            }
        }

        public object MapToEntityObject(object obj, Type objType)
        {
            if (ReflectionHelpers.IsCollectionOfEntities(objType)) throw new FormModelPropertyCantBeAutomapedException(string.Format("{0} can't be automapped to {1}", GetType().Name, objType.Name));

            object collection = obj;
            if (collection == null) collection = ReflectionHelpers.CreateGenericType(typeof(List<>), objType.GetGenericArguments()[0]);

            var repo = RepoFactory.CreateForRuntimeType(objType.GetGenericArguments()[0]);

            //first delete from collection things that are no longer selected
            collection.ExecuteMethod("Clear");
            //foreach (var toDelete in (collection as IEnumerable<object>).ToList()) collection.ExecuteMethod("Remove", toDelete);

            //Then add the new ones
            foreach (var option in Options)
            {
                if (!option.Selected) continue;
                var id = long.Parse(option.Value);

                var newEntity = (EntityCore)repo.ExecuteMethod("GetById", id);
                collection.ExecuteMethod("Add", newEntity);
            }
            return collection;
        }

        public int CompareTo(object obj)
        {
            var other = (MultiSelectFormModelBase)obj;
            if (Options.Count != other.Options.Count) return 1;

            foreach (var option in Options)
            {
                // ReSharper disable AccessToModifiedClosure
                if (other.Options.Find(x => x.Value == option.Value && x.Label == option.Label && x.Selected == option.Selected) == null) return 1;
                // ReSharper restore AccessToModifiedClosure
            }
            return 0;
        }

        public bool GetIEditorTemplateImplemented() { return true; }

        public abstract MvcHtmlString EditorTemplate(HtmlHelper html);

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (controllerContext == null) throw new ArgumentNullException("controllerContext");
            if (bindingContext == null) throw new ArgumentNullException("bindingContext");

            string key = bindingContext.ModelName;
            ValueProviderResult val = bindingContext.ValueProvider.GetValue(key);
            string attemptedValue;
            if (val == null || string.IsNullOrEmpty(val.AttemptedValue))
            {
                if (bindingContext.ModelMetadata.IsRequired) bindingContext.ModelState.AddModelError(key, string.Format("The field {0} is required", bindingContext.ModelMetadata.DisplayName ?? bindingContext.ModelMetadata.PropertyName));
                attemptedValue = "";
            }
            else
            {
                attemptedValue = val.AttemptedValue;
            }

            bindingContext.ModelState.SetModelValue(key, val);
            var attemptedValues = attemptedValue.Split(',');
            var existingModel = (MultiSelectFormModelBase)bindingContext.Model;
            if (existingModel != null)
            {
                //Clear out selected
                existingModel.Options.ForEach(x => x.Selected = false);
                foreach (var selectedValue in attemptedValues)
                {
                    // ReSharper disable AccessToModifiedClosure
                    var selectedOption = existingModel.Options.Find(x => x.Value == selectedValue);
                    // ReSharper restore AccessToModifiedClosure
                    if (selectedOption != null) selectedOption.Selected = true;
                }
                return existingModel;
            }

            Options.Clear();
            foreach (var value in attemptedValues) Options.Add(new Option(value, "N/A", true));
            return this;
        }
    }

    public class ListBoxFormModel : MultiSelectFormModelBase
    {
        public override MvcHtmlString EditorTemplate(HtmlHelper html)
        {
            return html.ListBoxFormModelEditor();
        }

    }

    public class CheckboxesListFormModel : MultiSelectFormModelBase
    {
        public string cssClass { get; set; }
        
        public override MvcHtmlString EditorTemplate(HtmlHelper html)
        {
            return html.CheckboxesListFormModelEditor(cssClass);
        }
    }
}
